package com.ophiux.common.swagger.config;

import com.ophiux.common.swagger.support.SwaggerProperties;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.web.*;
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.RequestParameterBuilder;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.schema.ScalarType;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.ApiSelectorBuilder;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.*;

/**
 * @desc: swagger3配置文件(这文件这里没有注册 ， 由 @ EnableSwagger3导入)
 * @author: hhl
 * @date：2022/7/8 16:18
 * @EnableOpenApi 开启swagger3的注解
 * @ConditionalOnProperty 读取配置swagger.enabled=true,如果是则加载此文件
 */
@RequiredArgsConstructor
@EnableOpenApi
@EnableConfigurationProperties(SwaggerProperties.class)
@ConditionalOnProperty(value = "swagger.enabled", havingValue = "true")
public class SwaggerAutoConfiguration {

    /**
     * swagger会解析的url规则
     */
    private static final String BASE_PATH = "/**";
    /**
     * 默认的排除路径，排除Spring Boot默认的错误处理路径和端点(在解析的url规则之上)
     * error，由于服务通常加前缀，所以前面/*忽略前缀
     */
    private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList("/error", "/actuator/**", "/*/error");

    private final SwaggerProperties swaggerProperties;

    //配置了Swagger的Docket的bean实例
    @Bean
    public Docket api(Environment environment) {
        if (swaggerProperties.getBasePath().isEmpty()) {
            swaggerProperties.getBasePath().add(BASE_PATH);
        }
        if (swaggerProperties.getExcludePath().isEmpty()) {
            swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH);
        }

        //根据环境判断是否开启swagger,如果是dev则开启
//        Profiles profiles = Profiles.of("dev");
//        boolean flag = environment.acceptsProfiles(profiles);

        Set<String> set = new HashSet<>();
        set.add("https");
        set.add("http");

        ApiSelectorBuilder builder = new Docket(DocumentationType.OAS_30).enable(swaggerProperties.getEnabled())
                .host(swaggerProperties.getHost())
                .apiInfo(apiInfo()).globalRequestParameters(getGlobalRequestParameters()).select()
                /**
                 * 配置扫描接口的方式
                 * 1、扫描方法上注解为GetMapping的请求
                 *      .apis(RequestHandlerSelectors.withMethodAnnotation(GetMapping.class))
                 * 2、扫描类上为Controller的所有类中的请求
                 *      .apis(RequestHandlerSelectors.withClassAnnotation(Controller.class))
                 * 3、什么也不扫描
                 *      .apis(RequestHandlerSelectors.none())
                 * 4、扫描所有请求
                 *      .apis(RequestHandlerSelectors.any())
                 */
                .apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()));
//            .apis(RequestHandlerSelectors.withClassAnnotation(Controller.class));
        //paths  循环过滤请求
        swaggerProperties.getBasePath().forEach(p -> builder.paths(PathSelectors.ant(p)));
        swaggerProperties.getExcludePath().forEach(p -> builder.paths(PathSelectors.ant(p).negate()));


        Docket docket = builder.build()
                // 支持的通讯协议集合
                .protocols(set)
                //为每个接口添加Authorization参数
//                .globalRequestParameters(getGlobalRequestParameters())
                // 授权信息设置，必要的header token等认证信息
//                .securitySchemes(securitySchemes())
                .securitySchemes(Collections.singletonList(securitySchema(swaggerProperties)))
                // 授权信息全局应用
//                .securityContexts(securityContexts())
                .securityContexts(Collections.singletonList(securityContext(swaggerProperties)))
                .pathMapping("/");

        return docket;
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder().title(swaggerProperties.getTitle()).description(swaggerProperties.getDescription())
                .license(swaggerProperties.getLicense()).licenseUrl(swaggerProperties.getLicenseUrl())
                .termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl())
                .contact(new Contact(swaggerProperties.getContact().getName(), swaggerProperties.getContact().getUrl(),
                        swaggerProperties.getContact().getEmail()))
                .version(swaggerProperties.getVersion()).build();
    }

    //生成全局通用header参数
    private List<RequestParameter> getGlobalRequestParameters() {
        List<RequestParameter> parameters = new ArrayList<>();
        parameters.add(new RequestParameterBuilder()
                .name("Authorization")
                .description("接口认证参数")
                .required(false)
                .in(ParameterType.HEADER)
                .query(q -> q.model(m -> m.scalarModel(ScalarType.STRING)))
                .build());

        parameters.add(new RequestParameterBuilder()
                .name("tenantId")
                .description("租户ID: 1")
                .required(false)
                .in(ParameterType.HEADER)
                .query(q -> q.model(m -> m.scalarModel(ScalarType.STRING)))
                .build());

//        parameters.add(new RequestParameterBuilder()
//                .name("VERSION")
//                .description("灰度路由版本信息")
//                .required(false)
//                .in(ParameterType.HEADER)
//                .query(q -> q.model(m -> m.scalarModel(ScalarType.STRING)))
//                .build());

        return parameters;
    }

    /**
     * 设置授权信息
     */
    private static OAuth securitySchema(SwaggerProperties swaggerProperties) {
        ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
        swaggerProperties.getAuthorization().getAuthorizationScopeList()
                .forEach(authorizationScope -> authorizationScopeList.add(
                        new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));
        ArrayList<GrantType> grantTypes = new ArrayList<>();
        swaggerProperties.getAuthorization().getTokenUrlList()
                .forEach(tokenUrl -> grantTypes.add(new ResourceOwnerPasswordCredentialsGrant(tokenUrl)));
        return new OAuth(swaggerProperties.getAuthorization().getName(), authorizationScopeList, grantTypes);
    }

    /**
     * 配置默认的全局鉴权策略的开关，通过正则表达式进行匹配；默认匹配所有URL
     *
     * @return
     */
    private static SecurityContext securityContext(SwaggerProperties swaggerProperties) {
        return SecurityContext.builder().securityReferences(defaultAuth(swaggerProperties)).build();
    }

    /**
     * 默认的全局鉴权策略
     *
     * @return
     */
    private static List<SecurityReference> defaultAuth(SwaggerProperties swaggerProperties) {
        ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
        //读取配置中的信息，放入authorizationScopeList集合
        swaggerProperties.getAuthorization().getAuthorizationScopeList()
                .forEach(authorizationScope -> authorizationScopeList.add(
                        new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[authorizationScopeList.size()];
        return Collections.singletonList(
                SecurityReference.builder().reference(swaggerProperties.getAuthorization().getName())
                        .scopes(authorizationScopeList.toArray(authorizationScopes)).build()
        );
    }


    /**
     * 设置授权信息
     */
    private List<SecurityScheme> securitySchemes() {
        List<ApiKey> result = new ArrayList<>();
        ApiKey apiKey = new ApiKey("Authorization", "Authorization", "Header");
        result.add(apiKey);
        return Collections.singletonList(apiKey);
    }

    /**
     * 授权信息全局应用
     */
    private List<SecurityContext> securityContexts() {
        return Collections.singletonList(
                SecurityContext.builder().securityReferences(
                        Collections.singletonList(
                                new SecurityReference("Authorization", new AuthorizationScope[]{new AuthorizationScope("global", "Authorization")})
                        )
                ).build()
        );
    }

}
